package org.common.netty;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.InetSocketAddress;
import java.util.HashMap;

@Slf4j
@Component
public class NettyClient {

    /**
     * 连接多个服务
     * @param hostStr "127.0.0.1:8081,127.0.0.1:8082"
     */
    public <T extends BaseNettyHandler> HashMap<String, Channel> start(String hostStr, Class<T> clz) {
        Constructor<T> declaredConstructor = null;
        try {
            declaredConstructor = clz.getDeclaredConstructor();
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
        declaredConstructor.setAccessible(true);
        HashMap<String, Channel> map = new HashMap<>();
        String[] hostArray = hostStr.split(",");
        if (hostArray.length == 0) {
            log.error("hostStr split error! hostStr = {}", hostStr);
            return map;
        }
        for (String host : hostArray) {
            String[] split = host.split(":");
            if (split.length != 2) {
                log.error("host list config error! host = {}", host);
                return map;
            }
            String ip = split[0];
            int port = Integer.parseInt(split[1]);
            BaseNettyHandler nettyHandler;
            try {
                nettyHandler = declaredConstructor.newInstance();
            } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
                throw new RuntimeException(e);
            }
            Channel channel = start(ip, port, nettyHandler);
            map.put(host, channel);
        }
        return map;
    }

    /**
     * 连接单个服务
     */
    public Channel start(String host, int port, BaseNettyHandler nettyHandler) {
        Channel channel;
        final EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group);
            bootstrap.channel(NioSocketChannel.class);
            bootstrap.handler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    ChannelPipeline pipeline = ch.pipeline();
                    NettyServer.tcpPipelineConfig(pipeline);
                    // ----------  自定义消息处理器 -----------
                    if (nettyHandler != null) {
                        pipeline.addLast(nettyHandler);
                    }
                }
            });
            ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port)).sync();
            channel = future.channel();

        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        Runtime.getRuntime().addShutdownHook(new Thread(group::shutdownGracefully));

        log.info("Start NettyClient ok! host = {}, port = {}", host, port);
        return channel;
    }

}
